/*
 ============================================================================
 Name        : LibreriaPPD.c
 Author      : SOogle
 Version     : 1.0
 Copyright   : All rights reserved
 Description : FUSELAGE - FUSE FAT32 FILE SYSTEM
 ============================================================================
 */

#include "../../headers/GlobalHeader.h"

extern struct_config configuracion;
extern int32_t algoritmoUtilizado;
extern pthread_mutex_t mutexCola, mutexArchivoMapeado;
extern PPD_CHS posicionActualCabezal, posicionAnterior, proximoSectorPlanifico;
extern int32_t contador_msync;
extern char *handlerArchivoMapeado;
extern t_log *logger;
int32_t filesize;

/*
 ============================================================================
 FUNCIONES DE SIMULACION
 ============================================================================
 */

float tiempoEntreSectores(PPD_CHS sector1,PPD_CHS sector2)
{
	float tiempoSectores=0;
	float tiempoQueTardaEntrePistas;
	float tiempoQueTardaEntreSectores;
	int tiempoPistas=0;
	int cantidadTotalSectores, cantidadPistas, cantidadSectores;
	float rpm;

	rpm=(float)configuracion.RPM;

	cantidadTotalSectores=configuracion.Cylinders*configuracion.Sectors;

	tiempoPistas=configuracion.TiempoEntrePistas;

	tiempoSectores=( 60000 / rpm ) / cantidadTotalSectores;

	cantidadPistas = (abs(sector1.cylinder-sector2.cylinder));
	cantidadSectores = diferenciaEntreSectores(sector1,sector2) - cantidadPistas;

	tiempoQueTardaEntrePistas=(float)cantidadPistas*tiempoPistas;

	tiempoQueTardaEntreSectores=(float)cantidadSectores*tiempoSectores;

	return (tiempoQueTardaEntrePistas+tiempoQueTardaEntreSectores);
}

int32_t *SectoresRecorro(PPD_CHS sectorAnterior, PPD_CHS sector)
{
	PPD_CHS copiaPosicionActual = sectorAnterior;
	int32_t *sectores;
	int32_t sectorAgrego, cantidadSectores, current;

	cantidadSectores = diferenciaEntreSectores(copiaPosicionActual, sector);

	current = 0;

	sectores = malloc(sizeof(int32_t)*cantidadSectores);

	//ME PARO EN LA MISMA PISTA DEL SECTOR AL QUE QUIERO LLEGAR
	while (sector.cylinder != copiaPosicionActual.cylinder)
	{
		//SI ESTOY EN UNA PISTA MENOR, INCREMENTO UNA PISTA
		if (sector.cylinder > copiaPosicionActual.cylinder)
		{
			copiaPosicionActual.cylinder = copiaPosicionActual.cylinder + 1;
			sectorAgrego = ppd_CHStoLBA(copiaPosicionActual);
			memcpy(sectores + current, &sectorAgrego, sizeof(int32_t));
			current++;
		}
		//SI ESTOY EN UNA PISTA MAYOR, DECREMENTO UNA PISTA
		else
		{
			copiaPosicionActual.cylinder = copiaPosicionActual.cylinder - 1;
			sectorAgrego = ppd_CHStoLBA(copiaPosicionActual);
			memcpy(sectores + current, &sectorAgrego, sizeof(int32_t));
			current++;
		}
	}

	//UNA VEZ QUE ESTOY EN LA MISMA PISTA, RECORRO HASTA LLEGAR AL SECTOR
	while (sector.sector != copiaPosicionActual.sector)
	{
		if (sector.sector < copiaPosicionActual.sector)
		{
			while(copiaPosicionActual.sector < configuracion.Sectors)
			{
				copiaPosicionActual.sector = copiaPosicionActual.sector + 1;
				sectorAgrego = ppd_CHStoLBA(copiaPosicionActual);
				memcpy(sectores + current, &sectorAgrego, sizeof(int32_t));
				current++;
			}

			copiaPosicionActual.sector = 1;
			sectorAgrego = ppd_CHStoLBA(copiaPosicionActual);
			memcpy(sectores + current, &sectorAgrego, sizeof(int32_t));
			current++;

		}
		else
		{
			copiaPosicionActual.sector = copiaPosicionActual.sector + 1;
			sectorAgrego = ppd_CHStoLBA(copiaPosicionActual);
			memcpy(sectores + current, &sectorAgrego, sizeof(int32_t));
			current++;
		}
	}
	return sectores;
}

int32_t diferenciaEntreSectores(PPD_CHS sector1,PPD_CHS sector2)
{
	int32_t resultado;

	resultado = abs(sector1.cylinder-sector2.cylinder);

	if(sector1.sector <= sector2.sector)
	{
		resultado = resultado + abs(sector2.sector-sector1.sector);
	}
	else
	{
		resultado = resultado + configuracion.Sectors-sector1.sector + sector2.sector;
	}

	return resultado;
}

PPD_CHS ppd_LBAToCHS(int32_t sectorRecibido)
{
	PPD_CHS estructuraCHS;
	int32_t cabezalesPorCilindro=0;
	int32_t sectoresPorPista=0;
	int32_t cilindrosTotales=0;
	int32_t temp=0;


	cilindrosTotales=configuracion.Cylinders;
	cabezalesPorCilindro=configuracion.Heads;
	sectoresPorPista=configuracion.Sectors;

	estructuraCHS.cylinder=(sectorRecibido/(sectoresPorPista*cabezalesPorCilindro));

	temp= (sectorRecibido) % ((cabezalesPorCilindro*sectoresPorPista));

	estructuraCHS.head=(sectorRecibido / sectoresPorPista ) % cabezalesPorCilindro;

	estructuraCHS.sector=sectorRecibido % configuracion.Sectors +1;

	return estructuraCHS;
}

int32_t ppd_CHStoLBA(PPD_CHS structRecibido)
{
	int32_t formula=0;
	int32_t cabezalesPorCilindro=0;
	int32_t sectoresPorPista=0;

	cabezalesPorCilindro=configuracion.Heads;
	sectoresPorPista=configuracion.Sectors;

	formula=((structRecibido.cylinder*cabezalesPorCilindro)+structRecibido.head)*sectoresPorPista+structRecibido.sector-1;

	return formula;
}

nipc procesoPedidoElegido(t_pedido pedidoPorProcesar, int32_t *colaLog)
{
	nipc paqueteRetorno = armoNIPC(pedidoPorProcesar);

	switch(algoritmoUtilizado)
	{
		case CON_INANICION:
			pthread_mutex_lock(&mutexCola);
			proximoSectorPlanifico = proximoPedido_SSTF();
			pthread_mutex_unlock(&mutexCola);
			break;

		case SIN_INANICION:
			pthread_mutex_lock(&mutexCola);
			proximoSectorPlanifico = proximoPedido_NSTEP();
			pthread_mutex_unlock(&mutexCola);
			break;
	}

	if(configuracion.LogActivado == 1)
	{
		logueoInformacionProceso(pedidoPorProcesar, colaLog);
	}

	posicionAnterior = posicionActualCabezal;
	posicionActualCabezal = ppd_LBAToCHS(pedidoPorProcesar.direccionSector);

	if(configuracion.LogActivado == 1)
	{
		log_info(logger, "MAIN", "Se proceso el pedido de %c sobre el sector %d:%d correctamente \n", pedidoPorProcesar.lectura_o_escritura, posicionActualCabezal.cylinder, posicionActualCabezal.sector);
	}

	return paqueteRetorno;
}

/*
 ============================================================================
 FUNCIONES QUE TRABAJAN SOBRE EL ARCHIVO MAPEADO
 ============================================================================
 */

char* ppd_abrirArchivo()
{
	char* discoMapeado;

	int32_t descriptorArchivo;
	int32_t retMadvise;

	//OBTENGO FILE DESCRIPTOR
	descriptorArchivo = open (configuracion.PathArchivo, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);

	//MAPEO DISCO
	discoMapeado = mmap(0, filesize*TO_BYTES,  PROT_READ | PROT_WRITE, MAP_SHARED, descriptorArchivo, 0);

	retMadvise = posix_madvise (discoMapeado, filesize*TO_BYTES, 2);

	return discoMapeado;
}

PPD_SECTOR_DEVUELVE ppd_leerSector(int32_t sectorARetornar,char* handlerArchivoMapeado)
{

	PPD_SECTOR_DEVUELVE sectorDevuelto;
	int32_t tiempoLectura=0;

	tiempoLectura=configuracion.TiempoLectura;

	pthread_mutex_lock (&mutexArchivoMapeado);
	memcpy(sectorDevuelto.bufferSector, getSector(handlerArchivoMapeado,sectorARetornar),512);
	pthread_mutex_unlock (&mutexArchivoMapeado);

	if (configuracion.TiempoLectura != 0)
	usleep(tiempoLectura);

	return sectorDevuelto;
}

int32_t ppd_escribirSector(int32_t sectorAEscribir, char* bufferSector,char* handlerArchivoMapeado)
{

	int32_t valorRetorno=TRUE;

	int32_t tiempoEscritura=0;

	tiempoEscritura=configuracion.TiempoEscritura;

	pthread_mutex_lock (&mutexArchivoMapeado);
	memcpy(getSector(handlerArchivoMapeado,sectorAEscribir), bufferSector,512);
	pthread_mutex_unlock (&mutexArchivoMapeado);

	if (configuracion.TiempoEscritura != 0)
	usleep(tiempoEscritura);

	return valorRetorno;
}

char* charLimpio()
{

	char *bufferSector=(char*)malloc(512*sizeof(char));

	memset( bufferSector, '\0', 512 );

	return bufferSector;
}

//DEVUELVE UN PUNTERO QUE APUNTA AL SECTOR PEDIDO EN EL DISCO
char *getSector(char * handlerDiscoMapeado, int32_t sector )
{
	return handlerDiscoMapeado + sector * 512;
}

int32_t tamanioDisco(){

	int32_t tamanio=0;
	int32_t cierroDescriptor=0;
	FILE *archivoDisco;

	struct stat buffer;
	int32_t status;

	archivoDisco = open(configuracion.PathArchivo, O_RDWR);

	status = fstat(archivoDisco, &buffer);

	cierroDescriptor=close(archivoDisco);

	tamanio = buffer.st_size / TO_BYTES;

	return tamanio;
}

int32_t cantidadTotalDeSectores()
{
	return (configuracion.Cylinders*configuracion.Sectors);
}

int32_t cerrarArchivo()
{
	int32_t returnValue;

	returnValue=munmap( handlerArchivoMapeado, filesize*TO_BYTES);

	return returnValue;
}

nipc armoNIPC(t_pedido pedidoPorProcesar)
{
	nipc paqueteRetorno;
	char bloque_lectura[BLOQUE_512];
	PPD_SECTOR_DEVUELVE sectorRetorno;

	if(pedidoPorProcesar.lectura_o_escritura=='L')
	{
		memcpy(pedidoPorProcesar.info,bloque_lectura,512);
		paqueteRetorno.type=LECTURA_OK;
		paqueteRetorno.payloadlength=sizeof(t_pedido);
		paqueteRetorno.payload=pedidoPorProcesar;
		sectorRetorno=ppd_leerSector(pedidoPorProcesar.direccionSector,handlerArchivoMapeado);
		memcpy(paqueteRetorno.payload.info,sectorRetorno.bufferSector,512);
	}
	else
	{
		int32_t valorEscritura=0;
		valorEscritura=ppd_escribirSector(pedidoPorProcesar.direccionSector, pedidoPorProcesar.info,handlerArchivoMapeado);
		if(valorEscritura==1){
			paqueteRetorno.type=ESCRITURA_OK;
			contador_msync++;
		}
		else
		{
			paqueteRetorno.type=ESCRITURA_ERROR;
		}
		paqueteRetorno.payloadlength=sizeof(t_pedido);
		paqueteRetorno.payload=pedidoPorProcesar;

	}

	return paqueteRetorno;
}
